package br.unb.graphics;

import java.awt.Component;
import java.awt.Insets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;

import java.util.Hashtable;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JRadioButton;
import javax.swing.JToolBar;

import resources.ResourceRoot;

public class InterfaceFactory {

    public static String SMALL_IMAGE = "smallImgPath";
    public static String BIG_IMAGE = "bigImgPath";
    /**
     * Suffix applied to the key used in resource file
     * lookups for an image.
     */
    public static final String imageSuffix = "Image";
    /**
     * Suffix applied to the key used in resource file
     * lookups for a submenu.
     */
    public static final String submenuSuffix = "Submenu";
    /**
     * Suffix applied to the key used in resource file
     * lookups for a label.
     */
    public static final String labelSuffix = "Label";
    /**
     * Suffix applied to the key used in resource file
     * lookups for an action.
     */
    public static final String actionSuffix = "Action";
    /**
     * Suffix applied to the key used in resource file
     * lookups for tooltip text.
     */
    public static final String tipSuffix = "Tooltip";
    private ResourceBundle resources;
    private Hashtable commands;
    private Hashtable menuItems;
//    private JTextComponent editor;
//    private JMenuBar menubar;
//    private JToolBar toolbar;
//    private JComponent status;
//    private JFrame elementTreeFrame;

    public InterfaceFactory(ResourceBundle resource, Action[] actions) {
        this.resources = resource;
        commands = new Hashtable();
        for (int i = 0; i < actions.length; i++) {
            Action a = actions[i];
            //commands.put(a.getText(Action.NAME), a);
            commands.put(a.getValue(Action.NAME), a);
        }
        menuItems = new Hashtable();
    }

    public InterfaceFactory(ResourceBundle resource) {
        this.resources = resource;
        commands = new Hashtable();
        menuItems = new Hashtable();
    }

    /**
     * This is the hook through which all menu items are
     * created.  It registers the result with the menuitem
     * hashtable so that it can be fetched with getMenuItem().
     * @see #getMenuItem
     */
    protected JMenuItem createMenuItem(String cmd) {
        JMenuItem mi = new JMenuItem(getResourceString(cmd + labelSuffix));
        URL url = getResourceByAddress(getResourceString(SMALL_IMAGE) + getResourceString(cmd + imageSuffix));

        if (url != null) {
            // url = this.getClass().getResource(url.toString());
            mi.setHorizontalTextPosition(JButton.RIGHT);
            mi.setIcon(new ImageIcon(url));
        }
        String astr = getResourceString(cmd + actionSuffix);
        if (astr == null) {
            astr = cmd;
        }
        mi.setActionCommand(astr);
        Action a = getAction(astr);
        if (a != null) {
            mi.addActionListener(a);
            a.addPropertyChangeListener(createActionChangeListener(mi));
            mi.setEnabled(a.isEnabled());
        } else {
            mi.setEnabled(false);
        }

        menuItems.put(cmd, mi);
        return mi;
    }

    /**
     * Fetch the menu item that was created for the given
     * command.
     * @param cmd  Name of the action.
     * @returns item created for the given command or null
     *  if one wasn't created.
     */
    public JMenuItem getMenuItem(String cmd) {
        return (JMenuItem) menuItems.get(cmd);
    }

    public Action getAction(String cmd) {
        return (Action) commands.get(cmd);
    }

    public String getResourceString(String nm) {
        String str;
        try {
            str = resources.getString(nm);
        } catch (MissingResourceException mre) {
            str = null;
            // mre.printStackTrace();
        }
        return str;
    }

    public URL getResourceByAddress(String address) {

        if (address != null) {
            URL url = ResourceRoot.class.getResource(address.substring(address.indexOf("/") + 1));

            return url;
        }
        return null;
    }

    public URL getResource(String key) {
        String name = getResourceString(key);

        if (name != null) {
            URL url = ResourceRoot.class.getResource(name.substring(name.indexOf("/") + 1));

            return url;
        }
        return null;
    }

    public ImageIcon getIcon(String address) {
        String file = getResourceString(address + imageSuffix);
        String path = getResourceString(SMALL_IMAGE);
        URL url = getResourceByAddress(path + file);
        if (url != null) {
            return new ImageIcon(url);
        } else {
            return null;
        }
    }

    public ImageIcon getBigIcon(String address) {
        String s = getResourceString(address + imageSuffix);
        String p = getResourceString(BIG_IMAGE);
        URL url = getResourceByAddress(p + s);
        if (url != null) {
            return new ImageIcon(url);
        } else {
            return null;
        }
    }

    /**
     * Create the toolbar.  By default this reads the
     * resource file for the definition of the toolbar.
     */
    public Component createToolbar() {
        JToolBar toolbar = new JToolBar();
        String[] toolKeys = tokenize(getResourceString("toolbar"));
        for (int i = 0; i < toolKeys.length; i++) {
            if (toolKeys[i].equals("-")) {
                toolbar.add(Box.createHorizontalStrut(5));
            } else {
                toolbar.add(createTool(toolKeys[i]));
            }
        }
        toolbar.add(Box.createHorizontalGlue());
        return toolbar;
    }

    /**
     * Hook through which every toolbar item is created.
     */
    protected Component createTool(String key) {
        return createToolbarButton(key);
    }

    /**
     * Create a button to go inside of the toolbar.  By default this
     * will load an image resource.  The image filename is relative to
     * the classpath (including the '.' directory if its a part of the
     * classpath), and may either be in a JAR file or a separate file.
     *
     * @param key The key in the resource file to serve as the basis
     *  of lookups.
     */
    protected JButton createToolbarButton(String key) {
        ImageIcon i = getBigIcon(key);
        JButton b = null;
        if (i != null) {
            b = new JButton(i) {

                public float getAlignmentY() {
                    return 0.5f;
                }
            };
        } else {
            b = new JButton(key);
        }
        b.setRequestFocusEnabled(false);
        b.setMargin(new Insets(1, 1, 1, 1));

        String astr = getResourceString(key + actionSuffix);
        if (astr == null) {
            astr = key;
        }
        Action a = getAction(astr);
        if (a != null) {
            b.setActionCommand(astr);
            b.addActionListener(a);
        } else {
            b.setEnabled(false);
        }

        String tip = getResourceString(key + tipSuffix);
        if (tip != null) {
            b.setToolTipText(tip);
        }

        return b;
    }

    /**
     * Take the given string and chop it up into a series
     * of strings on whitespace boundries.  This is useful
     * for trying to get an array of strings out of the
     * resource file.
     */
    protected String[] tokenize(String input) {
        Vector v = new Vector();
        StringTokenizer t = new StringTokenizer(input);
        String cmd[];

        while (t.hasMoreTokens()) {
            v.addElement(t.nextToken());
        }
        cmd = new String[v.size()];
        for (int i = 0; i < cmd.length; i++) {
            cmd[i] = (String) v.elementAt(i);
        }
        return cmd;
    }

    /**
     * Create the menubar for the app.  By default this pulls the
     * definition of the menu from the associated resource file.
     */
    public JMenuBar createMenubar() {

        JMenuBar mb = new JMenuBar();

        String[] menuKeys = tokenize(getResourceString("menubar"));
        for (int i = 0; i < menuKeys.length; i++) {
            JMenu m = createMenu(menuKeys[i]);
            if (m != null) {
                mb.add(m);
            }
        }
        return mb;
    }

    /**
     * Create a menu for the app.  By default this pulls the
     * definition of the menu from the associated resource file.
     */
    protected JMenu createMenu(String key) {
        String[] itemKeys = tokenize(getResourceString(key));
        JMenu menu = new JMenu(getResourceString(key + "Label"));
        for (int i = 0; i < itemKeys.length; i++) {
            if (itemKeys[i].equals("-")) {
                menu.addSeparator();
            } else if (getResourceString(itemKeys[i] + submenuSuffix) != null) {
                JMenu submenu = createMenu(itemKeys[i] + submenuSuffix);
                menu.add(submenu);
            } else {
                JMenuItem mi = createMenuItem(itemKeys[i]);
                menu.add(mi);
            }
        }
        return menu;
    }

    public JLabel createLabel(String strLabel) {
        String labelStr = getResourceString(strLabel);
        if (labelStr != null) {
            return new JLabel(labelStr);
        }
        return new JLabel("-null-");
    }

    public JButton createButton(String str) {
        String labelStr = getResourceString(str);
        String astr = getResourceString(str + actionSuffix);
        if (labelStr != null) {
            JButton jb;
            ImageIcon i = getBigIcon(str);
            if (i != null) {
                jb = new JButton(labelStr, i);
            } else {
                jb = new JButton(labelStr);
            }
            if (astr == null) {
                astr = str;
            }
            Action a = getAction(astr);
            if (a != null) {
                jb.setActionCommand(astr);
                jb.addActionListener(a);
            }
            return jb;
        }
        return new JButton("-null-");
    }

    public JRadioButton createRadioButton(String str, boolean isSelected) {

        String labelStr = getResourceString(str);
        String astr = getResourceString(str + actionSuffix);
        if (labelStr != null) {
            JRadioButton jrb;
            ImageIcon i = getBigIcon(str);
            if (i != null) {
                jrb = new JRadioButton(labelStr, i, isSelected);
            } else {
                jrb = new JRadioButton(labelStr, isSelected);
            }
            if (astr == null) {
                astr = str;
            }
            Action a = getAction(astr);
            if (a != null) {
                jrb.setActionCommand(astr);
                jrb.addActionListener(a);
            }
            return jrb;
        }
        return new JRadioButton("-null-");
    }

    public JCheckBox createCheckBox(String str, boolean isSelected) {
        String labelStr = getResourceString(str);
        String astr = getResourceString(str + actionSuffix);
        if (labelStr != null) {
            JCheckBox jcb;
            ImageIcon i = getBigIcon(str);
            if (i != null) {
                jcb = new JCheckBox(labelStr, i, isSelected);
            } else {
                jcb = new JCheckBox(labelStr, isSelected);
            }
            if (astr == null) {
                astr = str;
            }
            Action a = getAction(astr);
            if (a != null) {
                jcb.setActionCommand(astr);
                jcb.addActionListener(a);
            }
            return jcb;
        }
        return new JCheckBox("-null-");
    }

    // Yarked from JMenu, ideally this would be public.
    protected PropertyChangeListener createActionChangeListener(JMenuItem b) {
        return new ActionChangedListener(b);
    }

    // Yarked from JMenu, ideally this would be public.
    private class ActionChangedListener implements PropertyChangeListener {

        JMenuItem menuItem;

        ActionChangedListener(JMenuItem mi) {
            super();
            this.menuItem = mi;
        }

        public void propertyChange(PropertyChangeEvent e) {
            String propertyName = e.getPropertyName();
            if (e.getPropertyName().equals(Action.NAME)) {
                String text = (String) e.getNewValue();
                menuItem.setText(text);
            } else if (propertyName.equals("enabled")) {
                Boolean enabledState = (Boolean) e.getNewValue();
                menuItem.setEnabled(enabledState.booleanValue());
            }
        }
    }
}
